/***********************************************************************************
 * 文件名： smcp_link.c
 * 作者： 刘言
 * 版本： 4.1
 * 说明：
 * 		Smart Mist Communication Protocol (SMCP) 4.1 通信协议的链路层。
 *      接受来自物理层（端口驱动）的位流形式的数据，并封装成帧，传送到上一层；同样，也将来自
 *  上一层的数据帧，拆装为位流形式的数据转发到物理层。
 * 修改记录：
 * 	2021/4/12: 初版。 刘言。
 *  2024/3/12: 支持4.1。刘言。
***********************************************************************************/
#include "polsys.h"
#include "smcp.h"
#include "smcp_link.h"
#include "..\polsys_lib\polsys_lib.h"


void _Smcp_EvLinkEvent(smcp_event_t e);

// 属性
ALIGN(4)
static xdata u8 mRxBuff[_SMCP_LINK_RX_BUFF_SIZE];
static idata u16 mPkgLen;
static bit mRxFilish;
static idata u8 mRxFlag;
static idata u8 mFlagTimeout = 0;
static idata u16 mBuffIndex = 0;
static idata polsys_task_t mTask;

#ifdef _SMCP_LINK_USE_CRC16
static idata u16 mTargetLen;
#endif

#ifdef _SMCP_LINK_USE_BUFF_CHECK
static idata u16 mRxLen = 0; // mBuffIndex可能会被清零，单独记录已接收的长度
#endif


// 内部函数声明

static void Loop();


// 对外接口函数

IN_LINE void SmcpLink_Init()
{
    Port_Init();
    Polsys_TaskInit(&mTask, Loop, 10);
}

FORCE_IN_LINE const u8 *SmcpLink_GetReceiveData()
{
    return mRxBuff;
}

FORCE_IN_LINE u16 SmcpLink_GetLength()
{
    return mPkgLen;
}

FORCE_IN_LINE u8 SmcpLink_GetPortNum()
{
    return 0;
}

FORCE_IN_LINE void SmcpLink_ClearBuff()
{
#ifdef _SMCP_LINK_USE_BUFF_CHECK
    mBuffIndex = 0;
#endif
}

FORCE_IN_LINE void SmcpLink_SendByte(u8 b)
{
    Port_SendByte(b);
}

void SmcpLink_SendPkg(u16 lenght, u8 *dat)
{
#ifdef _SMCP_LINK_USE_CRC16
    u16 crc16 = Crc_GetCRC16(dat, lenght);
#endif
    Port_SendByte(0x55);
    Port_SendByte(0xAA);
    Port_SendByte(lenght >> 8); // 协议规定高字节在前
    Port_SendByte(lenght);
    Port_Send(dat, lenght);
#ifdef _SMCP_LINK_USE_CRC16
    Port_SendByte(crc16); // 协议规定低字节在前
    Port_SendByte(crc16 >> 8);
#endif
}

// 以下是需要外部调用的事件处理函数

// 由外部调用（一般是UART中断）来通知 Smcp 收到了新的数据（1个字节）。
// 外部调用事件：收到了新的数据（1个字节）
void SmcpLink_EvReceivedData(u8 dat)
{
    mFlagTimeout = 0; // 超时清零
    switch (mRxFlag)
    {
    case 0: //接收头H
        if (dat == 0x55)
        {
            mRxFlag = 1; //进入下一步
        }
        break;

    case 1: //接收头L
        if (dat == 0xAA)
        {
            mRxFlag = 2; //进入下一步
        }
        else if (dat == 0x55)
        {
            mRxFlag = 1; //继续这一步
        }
        else
        {
            mRxFlag = 0; //重新开始接收头H
        }
        break;

    case 2: // 接收长度H
        mPkgLen = (u16)dat << 8;
        mRxFlag = 3;
        break;

    case 3: // 接收长度L
        mPkgLen += dat;
        if (mPkgLen == 0)
            mRxFlag = 0; //重新开始接收头H
        else
        {
#ifdef _SMCP_LINK_USE_CRC16
            mTargetLen = mPkgLen + 2;
#endif
            mRxFlag = 4;
        }
        mBuffIndex = 0;
        break;

    case 4: //接收数据
        mRxBuff[mBuffIndex++] = dat;
#ifdef _SMCP_LINK_USE_BUFF_CHECK
        if (mBuffIndex == _SMCP_LINK_RX_BUFF_SIZE)
        {
            mCall(SMCP_EV_BUFF_FULL);
            mBuffIndex = 0;
        }
        mRxLen++; // mBuffIndex会被清零，单独记录已接收的长度
#ifdef _SMCP_LINK_USE_CRC16
        if (mRxLen == mTargetLen) //接收完毕
#else
        if (mRxLen == mPkgLen) //接收完毕
#endif
#else
#ifdef _SMCP_LINK_USE_CRC16
    if (mBuffIndex == mTargetLen) //接收完毕
#else
    if (mBuffIndex == mPkgLen) //接收完毕
#endif
#endif
        {
            mRxFilish = true; //标记一包接收完毕
            mRxFlag = 0;      //重新开始接收头H
            mBuffIndex = 0;
            Polsys_SetTaskReady(&mTask);    // 立即进行处理
#ifdef _SMCP_LINK_USE_BUFF_CHECK
            mRxLen = 0;
#endif
        }
        break;

    default:
        mRxFlag = 0;
        mBuffIndex = 0;
#ifdef _SMCP_LINK_USE_BUFF_CHECK
        mRxLen = 0;
#endif
        break;
    }
}


// 10ms循环执行
static void Loop()
{
    // 接收超时控制
    if (mRxFlag > 0) // 正在接收
    {
        mFlagTimeout++;
        if (mFlagTimeout == 10) // 100ms未收到数据
        {
            _Smcp_EvLinkEvent(SMCP_EV_TIME_OUT); // 上报：接收超时
            mFlagTimeout = 0;
            mRxFlag = 0;
            mBuffIndex = 0;
        }
    }
    else
        mFlagTimeout = 0;
    // 接收完毕处理
    if (mRxFilish)
    {
        mRxFilish = false;
#ifdef _SMCP_LINK_USE_CRC16
        //if(Crc_GetCRC16(mRxBuff,mPkgLen) != *(u16 *)(mRxBuff + mPkgLen))
        if (Crc_Verify16(mRxBuff, mTargetLen) == false)
            _Smcp_EvLinkEvent(SMCP_EV_CHECK_ERROR); // 上报：校验错误
        else
#endif
        {
            _Smcp_EvLinkEvent(SMCP_EV_RECEIVED_PKG);    // 上报：收到了一包数据
        }
    }
}




